//go:build !nomsgpack
// +build !nomsgpack

package bind

import (
	"net/http"
)

// 最常见的数据格式的 Content-Type MIME。
const (
	MiMeJSON              = "application/json"
	MiMeHTML              = "text/html"
	MiMeXML               = "application/xml"
	MiMeXML2              = "text/xml"
	MiMePlain             = "text/plain"
	MiMePOSTForm          = "application/x-www-form-urlencoded"
	MiMeMultipartPOSTForm = "multipart/form-data"
	MiMePROTOBUF          = "application/x-protobuf"
	MiMeMSGPACK           = "application/x-msgpack"
	MiMeMSGPACK2          = "application/msgpack"
	MiMeYAML              = "application/x-yaml"
)

// Validator 是实现 IStructValidator 接口的默认验证器。
var Validator IValidator = &gValidValidator{}
//var Validator IValidator = &defaultValidator{}

// 这些实现了 IBind 接口，可用于将请求中存在的数据绑定到struct实例。
var (
	JSON          = jsonBinding{}
	XML           = xmlBinding{}
	Form          = formBinding{}
	Query         = queryBinding{}
	FormPost      = formPostBinding{}
	FormMultipart = formMultipartBinding{}
	ProtoBuf      = protobufBinding{}
	MsgPack       = msgpackBinding{}
	YAML          = yamlBinding{}
	Uri           = uriBinding{}
	Header        = headerBinding{}
)

// IBind 定义一个绑定接口，用于绑定请求中存在的数据（例如JSON请求正文，查询参数或POST形式)，
// 这个接口的实例将会被 *gin.context的 ShouldBind等方法调用。
type IBind interface {
	Name() string
	Bind(*http.Request, interface{}) error
}

// IBindBody 从提供的字节而不是req.Body中读取正文。
type IBindBody interface {
	IBind
	BindBody([]byte, interface{}) error
}

// IBindUri 从uri中读取参数，实行绑定<params<参数>
type IBindUri interface {
	Name() string
	BindUri(map[string][]string, interface{}) error
}

// IValidator 参数验证接口，验证请求的参数。
type IValidator interface {
	// Validate 可以接收任何类型的数据，即使配置不正确，也永远不会panic。
	//  如果接收到的类型是切片，则应在每个元素上进行验证。
	//  如果接收到的类型不是struct或slice或array，则应跳过任何验证，并且必须返回nil。
	//  如果接收到的类型是 struct/*struct，则应执行验证。
	//  如果该结构体无效或验证本身失败，则应返回描述性错误。
	//  否则必须返回nil。
	Validate(interface{}) error
	// Engine 返回实现了 IStructValidator 接口的验证器。
	Engine() interface{}
}

// Default 根据HTTP方法和内容类型返回适当的 IBind 实例。
func Default(method, contentType string) IBind {
	if method == http.MethodGet {
		return Form
	}
	switch contentType {
	case MiMeJSON:
		return JSON
	case MiMeXML, MiMeXML2:
		return XML
	case MiMePROTOBUF:
		return ProtoBuf
	case MiMeMSGPACK, MiMeMSGPACK2:
		return MsgPack
	case MiMeYAML:
		return YAML
	case MiMeMultipartPOSTForm:
		return FormMultipart
	default: // case MIMEPOSTForm:
		return Form
	}
}

func DefaultBody(name string) IBindBody {
	switch name {
	case "json":
		return jsonBinding{}
	case "xml":
		return xmlBinding{}
	case "protobuf":
		return protobufBinding{}
	case "msgpack":
		return msgpackBinding{}
	case "yaml":
		return yamlBinding{}
	default: // case MIMEPOSTForm:
		return jsonBinding{}
	}
}

// 这是给 &defaultValidator{} 用的。
//func validate(obj interface{}) error {
//	if Validator == nil {
//		return nil
//	}
//	return Validator.Validate(obj)
//}

// 这是给 &gValidValidator{} 用的。
func validate(obj interface{}) error {
	err :=Validator.Validate(obj)
	if err.Error() == "" {
		return nil
	}
	return err
}